/*
 * printercache.cpp
 *
 *  Created on: Oct 12, 2011
 *      Author: welcome
 */
#include <KD/kd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <printercache.h>
#include <stdlib.h>
#include <string.h>
#include <rdp.h>

#define rmdir(a) kdRmdir(a)

static RD_BOOL printercache_mkdir(char *base, char *printer) {
	char *path;

	path = (char *) kdMalloc(strlen(base) + sizeof("/.rdesktop/rdpdr/")
			+ strlen(printer) + 1);

	sprintf(path, "%s/.rdesktop", base);
	if ((mkdir(path, 0700) == -1) && errno != EEXIST) {
		perror(path);
		kdFree(path);
		return False;
	}

	strcat(path, "/rdpdr");
	if ((mkdir(path, 0700) == -1) && errno != EEXIST) {
		perror(path);
		kdFree(path);
		return False;
	}

	strcat(path, "/");
	strcat(path, printer);
	if ((mkdir(path, 0700) == -1) && errno != EEXIST) {
		perror(path);
		kdFree(path);
		return False;
	}

	kdFree(path);
	return True;
}

static RD_BOOL printercache_unlink_blob(char *printer) {
	char *path;
	char *home;

	if (printer == NULL)
		return False;

	home = getenv("HOME");
	if (home == NULL)
		return False;

	path = (char *) kdMalloc(strlen(home) + sizeof("/.rdesktop/rdpdr/")
			+ strlen(printer) + sizeof("/AutoPrinterCacheData") + 1);

	sprintf(path, "%s/.rdesktop/rdpdr/%s/AutoPrinterCacheData", home, printer);

	if (unlink(path) < 0) {
		kdFree(path);
		return False;
	}

	sprintf(path, "%s/.rdesktop/rdpdr/%s", home, printer);

	if (rmdir(path) < 0) {
		kdFree(path);
		return False;
	}

	kdFree(path);
	return True;
}

static RD_BOOL printercache_rename_blob(char *printer, char *new_printer) {
	char *printer_path;
	char *new_printer_path;
	int printer_maxlen;

	char *home;

	if (printer == NULL)
		return False;

	home = getenv("HOME");
	if (home == NULL)
		return False;

	printer_maxlen = (strlen(printer) > strlen(new_printer) ? strlen(printer)
			: strlen(new_printer)) + strlen(home) + sizeof("/.rdesktop/rdpdr/")
			+ 1;

	printer_path = (char *) kdMalloc(printer_maxlen);
	new_printer_path = (char *) kdMalloc(printer_maxlen);

	sprintf(printer_path, "%s/.rdesktop/rdpdr/%s", home, printer);
	sprintf(new_printer_path, "%s/.rdesktop/rdpdr/%s", home, new_printer);

	printf("%s,%s\n", printer_path, new_printer_path);
	if (rename(printer_path, new_printer_path) < 0) {
		kdFree(printer_path);
		kdFree(new_printer_path);
		return False;
	}

	kdFree(printer_path);
	kdFree(new_printer_path);
	return True;
}

int printercache_load_blob(char *printer_name, uint8 ** data) {
	char *home, *path;
	struct stat st;
	int fd, length;

	if (printer_name == NULL)
		return 0;

	*data = NULL;

	home = getenv("HOME");
	if (home == NULL)
		return 0;

	path = (char *) kdMalloc(strlen(home) + sizeof("/.rdesktop/rdpdr/")
			+ strlen(printer_name) + sizeof("/AutoPrinterCacheData") + 1);
	sprintf(path, "%s/.rdesktop/rdpdr/%s/AutoPrinterCacheData", home,
			printer_name);

	fd = open(path, O_RDONLY);
	if (fd == -1) {
		kdFree(path);
		return 0;
	}

	if (fstat(fd, &st)) {
		kdFree(path);
		return 0;
	}

	*data = (uint8 *) kdMalloc(st.st_size);
	length = read(fd, *data, st.st_size);
	close(fd);
	kdFree(path);
	return length;
}

static void printercache_save_blob(char *printer_name, uint8 * data,
		uint32 length) {
	char *home, *path;
	int fd;

	if (printer_name == NULL)
		return;

	home = getenv("HOME");
	if (home == NULL)
		return;

	if (!printercache_mkdir(home, printer_name))
		return;

	path = (char *) kdMalloc(strlen(home) + sizeof("/.rdesktop/rdpdr/")
			+ strlen(printer_name) + sizeof("/AutoPrinterCacheData") + 1);
	sprintf(path, "%s/.rdesktop/rdpdr/%s/AutoPrinterCacheData", home,
			printer_name);

	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
	if (fd == -1) {
		perror(path);
		kdFree(path);
		return;
	}

	if (write(fd, data, length) != length) {
		perror(path);
		unlink(path);
	}

	close(fd);
	kdFree(path);
}

void printercache_process(STREAM s) {
	uint32 type, printer_length, driver_length, printer_unicode_length,
			blob_length;
	char device_name[9], printer[256], driver[256];

	in_uint32_le(s, type);
	switch (type) {
	case 4: /* rename item */
		in_uint8(s, printer_length);
		in_uint8s(s, 0x3); /* padding */
		in_uint8(s, driver_length);
		in_uint8s(s, 0x3); /* padding */

		/* NOTE - 'driver' doesn't contain driver, it contains the new printer name */

		rdp_in_unistr(s, printer, sizeof(printer), printer_length);
		rdp_in_unistr(s, driver, sizeof(driver), driver_length);

		printercache_rename_blob(printer, driver);
		break;

	case 3: /* delete item */
		in_uint8(s, printer_unicode_length);
		in_uint8s(s, 0x3); /* padding */
		rdp_in_unistr(s, printer, sizeof(printer), printer_unicode_length);
		printercache_unlink_blob(printer);
		break;

	case 2: /* save printer data */
		in_uint32_le(s, printer_unicode_length);
		in_uint32_le(s, blob_length);

		if (printer_unicode_length < 2 * 255) {
			rdp_in_unistr(s, printer, sizeof(printer), printer_unicode_length);
			printercache_save_blob(printer, s->p, blob_length);
		}
		break;

	case 1: /* save device data */
		in_uint8a(s, device_name, 5); /* get LPTx/COMx name */

		/* need to fetch this data so that we can get the length of the packet to store. */
		in_uint8s(s, 0x2); /* ??? */
		in_uint8s(s, 0x2) /* pad?? */
		in_uint32_be(s, driver_length);
		in_uint32_be(s, printer_length);
		in_uint8s(s, 0x7) /* pad?? */

		s->p = s->p - 19;

		printercache_save_blob(device_name, s->p, driver_length
				+ printer_length + 19);
		break;
	default:

		unimpl("RDPDR Printer Cache Packet Type: %d\n", type);
		break;
	}
}
